<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>2048 游戏</title>
    <style>
        /* 内联样式，避免加载外部CSS文件 */
        :root {
            --bg-color: #faf8ef;
            --grid-color: #bbada0;
            --cell-empty-color: #cdc1b4;
            --text-dark: #776e65;
            --text-light: #f9f6f2;
            --button-bg: #8f7a66;
            --button-hover: #9f8b76;
            --shadow-color: rgba(0, 0, 0, 0.15);
        }

        body {
            font-family: 'Arial', 'Helvetica', sans-serif;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
            background-color: var(--bg-color);
            background-image: linear-gradient(to bottom right, #faf8ef, #f5e8d3);
        }

        #game-container {
            text-align: center;
            background-color: rgba(255, 255, 255, 0.8);
            padding: 20px;
            border-radius: 16px;
            box-shadow: 0 8px 24px var(--shadow-color);
            max-width: 500px;
            width: 100%;
        }

        .game-header {
            display: flex;
            justify-content: space-between;
            margin-bottom: 20px;
            padding: 15px;
            background: rgba(180, 160, 145, 0.15);
            border-radius: 12px;
            box-shadow: 0 4px 8px var(--shadow-color);
        }

        .game-info {
            padding: 10px 18px;
            background: var(--button-bg);
            color: var(--text-light);
            border-radius: 8px;
            font-weight: bold;
            box-shadow: 0 4px 8px var(--shadow-color);
            transition: all 0.3s ease;
            font-size: 16px;
        }

        .game-info:hover {
            transform: translateY(-2px);
            box-shadow: 0 6px 12px var(--shadow-color);
        }

        .mode-selector {
            margin-bottom: 20px;
            display: flex;
            justify-content: center;
            gap: 10px;
            flex-wrap: wrap;
        }

        .mode-btn {
            padding: 10px 16px;
            background: var(--button-bg);
            color: var(--text-light);
            border: none;
            border-radius: 8px;
            cursor: pointer;
            transition: all 0.3s ease;
            font-weight: bold;
            box-shadow: 0 4px 8px var(--shadow-color);
        }

        .mode-btn:hover {
            background: var(--button-hover);
            transform: translateY(-3px);
            box-shadow: 0 6px 12px var(--shadow-color);
        }

        .mode-btn.active {
            background: var(--text-dark);
            transform: translateY(-2px);
        }

        #gameCanvas {
            background: var(--grid-color);
            border-radius: 12px;
            padding: 10px;
            margin: 20px 0;
            box-shadow: 0 6px 16px var(--shadow-color);
            transition: all 0.3s ease;
        }

        #new-game-btn {
            padding: 12px 24px;
            font-size: 18px;
            background: var(--button-bg);
            color: var(--text-light);
            border: none;
            border-radius: 8px;
            cursor: pointer;
            transition: all 0.3s ease;
            box-shadow: 0 4px 12px var(--shadow-color);
            margin-top: 10px;
            font-weight: bold;
        }

        #new-game-btn:hover {
            background: var(--button-hover);
            transform: translateY(-3px);
            box-shadow: 0 6px 16px var(--shadow-color);
        }

        .direction-buttons {
            margin-top: 20px;
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 10px;
        }

        .horizontal-buttons {
            display: flex;
            gap: 40px;
        }

        .dir-btn {
            width: 60px;
            height: 60px;
            background: var(--button-bg);
            color: var(--text-light);
            border: none;
            border-radius: 12px;
            cursor: pointer;
            font-size: 26px;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: all 0.3s ease;
            box-shadow: 0 4px 8px var(--shadow-color);
        }

        .dir-btn:hover {
            background: var(--button-hover);
            transform: scale(1.1);
            box-shadow: 0 6px 12px var(--shadow-color);
        }
        
        .dir-btn:active {
            transform: scale(0.95);
            box-shadow: 0 2px 4px var(--shadow-color);
        }

        /* 修改媒体查询规则 */
        @media (min-width: 768px) and (orientation: landscape) {
            .direction-buttons {
                display: none;
            }
        }
        
        @media (max-width: 500px) {
            #game-container {
                padding: 10px;
            }
            
            .game-header {
                padding: 10px;
            }
            
            .mode-btn {
                padding: 8px 12px;
                font-size: 14px;
            }
            
            #gameCanvas {
                margin: 10px 0;
            }
        }

        /* 添加移动瓷砖的动画 */
        @keyframes pop {
            0% { transform: scale(0.8); }
            50% { transform: scale(1.1); }
            100% { transform: scale(1); }
        }
        
        @keyframes fade-in {
            from { opacity: 0; }
            to { opacity: 1; }
        }
        
        @keyframes glow {
            0% { box-shadow: 0 0 5px rgba(237, 194, 46, 0.5); }
            50% { box-shadow: 0 0 20px rgba(237, 194, 46, 0.8); }
            100% { box-shadow: 0 0 5px rgba(237, 194, 46, 0.5); }
        }
    </style>
</head>
<body>
    <div id="game-container">
        <div class="game-header">
            <div id="score" class="game-info">分数: 0</div>
            <div id="timer" class="game-info">时间: 0秒</div>
        </div>
        
        <div class="mode-selector">
            <button class="mode-btn active" onclick="game.changeMode('normal')">普通模式</button>
            <button class="mode-btn" onclick="game.changeMode('time_challenge', 60)">1分钟挑战</button>
            <button class="mode-btn" onclick="game.changeMode('time_challenge', 300)">5分钟挑战</button>
            <button class="mode-btn" onclick="game.changeMode('time_challenge', 600)">10分钟挑战</button>
        </div>

        <canvas id="gameCanvas" width="400" height="400"></canvas>
        <button id="new-game-btn">新游戏</button>
        
        <!-- 添加方向按钮 -->
        <div class="direction-buttons">
            <button class="dir-btn up" onclick="game.move('up')">↑</button>
            <div class="horizontal-buttons">
                <button class="dir-btn left" onclick="game.move('left')">←</button>
                <button class="dir-btn right" onclick="game.move('right')">→</button>
            </div>
            <button class="dir-btn down" onclick="game.move('down')">↓</button>
        </div>
    </div>

    <script>
        const GRID_SIZE = 4;
        const CELL_SIZE = 100;
        const CELL_SPACING = 10;
        const ANIMATION_DURATION = 150; // 动画持续时间（毫秒）

        class Game2048 {
            constructor() {
                this.canvas = document.getElementById('gameCanvas');
                this.ctx = this.canvas.getContext('2d');
                this.grid = Array(GRID_SIZE).fill().map(() => Array(GRID_SIZE).fill(0));
                this.previousGrid = null; // 用于存储上一个状态，以便进行动画
                this.previousPositions = null; // 存储方块之前的位置
                this.animationInProgress = false; // 动画是否正在进行
                this.animationStartTime = 0; // 动画开始时间
                this.animationProgress = 0; // 动画进度
                this.newTiles = []; // 新生成的瓷砖位置
                this.mergedTiles = []; // 合并的瓷砖位置
                this.movedTiles = []; // 移动的瓷砖位置和方向
                this.score = 0;
                this.previousScore = 0; // 记录之前的分数
                this.startTime = Date.now();
                this.gameMode = 'normal';
                this.timeLimit = null;
                this.colors = {
                    2: "#eee4da",
                    4: "#ede0c8",
                    8: "#f2b179",
                    16: "#f59563",
                    32: "#f67c5f",
                    64: "#f65e3b",
                    128: "#edcf72",
                    256: "#edcc61",
                    512: "#edc850",
                    1024: "#edc53f",
                    2048: "#edc22e"
                };
                this.textColors = {
                    2: "#776e65",
                    4: "#776e65",
                    8: "#f9f6f2",
                    16: "#f9f6f2",
                    32: "#f9f6f2",
                    64: "#f9f6f2",
                    128: "#f9f6f2",
                    256: "#f9f6f2",
                    512: "#f9f6f2",
                    1024: "#f9f6f2",
                    2048: "#f9f6f2"
                };

                this.init();
                this.setupEventListeners();
                
                // 设置动画循环
                requestAnimationFrame(this.animate.bind(this));
            }

            init() {
                this.grid = Array(GRID_SIZE).fill().map(() => Array(GRID_SIZE).fill(0));
                this.previousGrid = null;
                this.previousPositions = null;
                this.score = 0;
                this.previousScore = 0;
                this.startTime = Date.now();
                this.newTiles = [];
                this.mergedTiles = [];
                this.movedTiles = [];
                this.addNewTile();
                this.addNewTile();
                this.draw();
                this.updateScore();
            }

            addNewTile() {
                const emptyCells = [];
                for (let i = 0; i < GRID_SIZE; i++) {
                    for (let j = 0; j < GRID_SIZE; j++) {
                        if (this.grid[i][j] === 0) {
                            emptyCells.push([i, j]);
                        }
                    }
                }
                if (emptyCells.length) {
                    const [i, j] = emptyCells[Math.floor(Math.random() * emptyCells.length)];
                    this.grid[i][j] = Math.random() < 0.9 ? 2 : 4;
                    this.newTiles.push([i, j]); // 记录新生成的瓷砖
                }
            }

            draw() {
                this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
                
                // 首先绘制背景网格
                for (let i = 0; i < GRID_SIZE; i++) {
                    for (let j = 0; j < GRID_SIZE; j++) {
                        const x = j * CELL_SIZE + CELL_SPACING;
                        const y = i * CELL_SIZE + CELL_SPACING;
                        const width = CELL_SIZE - 2 * CELL_SPACING;
                        const height = CELL_SIZE - 2 * CELL_SPACING;

                        // 绘制空格背景
                        this.ctx.fillStyle = '#cdc1b4';
                        this.drawRoundedRect(x, y, width, height, 8);
                    }
                }
                
                // 然后绘制数字瓷砖
                for (let i = 0; i < GRID_SIZE; i++) {
                    for (let j = 0; j < GRID_SIZE; j++) {
                        const value = this.grid[i][j];
                        if (value === 0) continue; // 跳过空格
                        
                        let x = j * CELL_SIZE + CELL_SPACING;
                        let y = i * CELL_SIZE + CELL_SPACING;
                        const width = CELL_SIZE - 2 * CELL_SPACING;
                        const height = CELL_SIZE - 2 * CELL_SPACING;
                        
                        let scale = 1;
                        let opacity = 1;
                        let glow = false;
                        
                        // 应用平移动画 - 检查是否是移动的瓷砖
                        const movedTile = this.movedTiles.find(tile => 
                            tile.toRow === i && tile.toCol === j);
                        
                        if (movedTile && this.animationInProgress) {
                            // 计算起始位置和目标位置
                            const fromX = movedTile.fromCol * CELL_SIZE + CELL_SPACING;
                            const fromY = movedTile.fromRow * CELL_SIZE + CELL_SPACING;
                            
                            // 应用动画进度进行插值
                            x = fromX + (x - fromX) * this.animationProgress;
                            y = fromY + (y - fromY) * this.animationProgress;
                        }
                        
                        // 检查是否是新瓷砖或合并的瓷砖
                        const isNewTile = this.newTiles.some(([row, col]) => row === i && col === j);
                        const isMergedTile = this.mergedTiles.some(([row, col]) => row === i && col === j);
                        
                        if (isNewTile) {
                            scale = this.easeOutBack(this.animationProgress);
                        } else if (isMergedTile) {
                            scale = 1 + 0.2 * Math.sin(Math.PI * this.animationProgress);
                            glow = value >= 128;
                        }

                        // 应用缩放和动画效果
                        this.ctx.save();
                        this.ctx.globalAlpha = opacity;
                        this.ctx.translate(x + width/2, y + height/2);
                        this.ctx.scale(scale, scale);
                        this.ctx.translate(-(x + width/2), -(y + height/2));
                        
                        // 绘制瓷砖背景
                        this.ctx.fillStyle = this.colors[value] || "#edc22e";
                        
                        // 为高值瓷砖添加发光效果
                        if (glow) {
                            this.ctx.shadowColor = "rgba(237, 194, 46, 0.8)";
                            this.ctx.shadowBlur = 15;
                        }
                        
                        this.drawRoundedRect(x, y, width, height, 8);
                        
                        // 绘制数字
                        this.ctx.fillStyle = this.textColors[value] || "#f9f6f2";
                        this.ctx.font = `bold ${value < 1024 ? 36 : 30}px Arial`;
                        this.ctx.textAlign = 'center';
                        this.ctx.textBaseline = 'middle';
                        this.ctx.shadowColor = "transparent";
                        this.ctx.shadowBlur = 0;
                        this.ctx.fillText(value.toString(), x + width/2, y + height/2);
                        
                        this.ctx.restore();
                    }
                }
            }
            
            // 绘制圆角矩形的辅助方法
            drawRoundedRect(x, y, width, height, radius) {
                this.ctx.beginPath();
                this.ctx.moveTo(x + radius, y);
                this.ctx.lineTo(x + width - radius, y);
                this.ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
                this.ctx.lineTo(x + width, y + height - radius);
                this.ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
                this.ctx.lineTo(x + radius, y + height);
                this.ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
                this.ctx.lineTo(x, y + radius);
                this.ctx.quadraticCurveTo(x, y, x + radius, y);
                this.ctx.closePath();
                this.ctx.fill();
            }
            
            // 动画缓动函数
            easeOutBack(t) {
                const c1 = 1.70158;
                const c3 = c1 + 1;
                return 1 + c3 * Math.pow(t - 1, 3) + c1 * Math.pow(t - 1, 2);
            }
            
            // 动画循环
            animate(timestamp) {
                if (this.animationInProgress) {
                    const elapsed = timestamp - this.animationStartTime;
                    this.animationProgress = Math.min(elapsed / ANIMATION_DURATION, 1);
                    
                    this.draw();
                    
                    if (this.animationProgress >= 1) {
                        this.animationInProgress = false;
                        this.newTiles = [];
                        this.mergedTiles = [];
                    }
                } else {
                    this.draw();
                }
                
                requestAnimationFrame(this.animate.bind(this));
            }

            updateScore() {
                const scoreElement = document.getElementById('score');
                scoreElement.textContent = `分数: ${this.score}`;
                
                // 只在分数增长时添加动画效果
                if (this.score > this.previousScore) {
                    scoreElement.style.animation = 'none';
                    void scoreElement.offsetWidth;
                    scoreElement.style.animation = 'pop 0.5s ease';
                    this.previousScore = this.score;
                }
            }

            setupEventListeners() {
                document.addEventListener('keydown', (event) => {
                    if (event.key.startsWith('Arrow')) {
                        event.preventDefault();
                        const direction = event.key.replace('Arrow', '').toLowerCase();
                        this.move(direction);
                    }
                });

                document.getElementById('new-game-btn').addEventListener('click', () => {
                    this.init();
                });

                // 添加触摸事件处理
                let touchStartX = 0;
                let touchStartY = 0;
                let touchEndX = 0;
                let touchEndY = 0;

                this.canvas.addEventListener('touchstart', (event) => {
                    event.preventDefault();
                    touchStartX = event.touches[0].clientX;
                    touchStartY = event.touches[0].clientY;
                }, false);

                this.canvas.addEventListener('touchmove', (event) => {
                    event.preventDefault();
                }, false);

                this.canvas.addEventListener('touchend', (event) => {
                    event.preventDefault();
                    touchEndX = event.changedTouches[0].clientX;
                    touchEndY = event.changedTouches[0].clientY;

                    const deltaX = touchEndX - touchStartX;
                    const deltaY = touchEndY - touchStartY;
                    const minSwipeDistance = 30; // 最小滑动距离

                    // 确定滑动方向
                    if (Math.abs(deltaX) > Math.abs(deltaY)) {
                        // 水平滑动
                        if (Math.abs(deltaX) > minSwipeDistance) {
                            if (deltaX > 0) {
                                this.move('right');
                            } else {
                                this.move('left');
                            }
                        }
                    } else {
                        // 垂直滑动
                        if (Math.abs(deltaY) > minSwipeDistance) {
                            if (deltaY > 0) {
                                this.move('down');
                            } else {
                                this.move('up');
                            }
                        }
                    }
                }, false);
            }

            changeMode(mode, timeLimit = null) {
                // 更新按钮状态
                document.querySelectorAll('.mode-btn').forEach(btn => {
                    btn.classList.remove('active');
                    if ((mode === 'normal' && btn.textContent === '普通模式') ||
                        (mode === 'time_challenge' && timeLimit && 
                         btn.textContent.includes(timeLimit/60))) {
                        btn.classList.add('active');
                    }
                });

                this.gameMode = mode;
                this.timeLimit = timeLimit;
                this.init();
            }

            move(direction) {
                if (this.animationInProgress) return; // 如果动画正在进行，忽略输入
                
                // 保存当前网格状态和位置用于动画
                this.previousGrid = JSON.parse(JSON.stringify(this.grid));
                this.previousPositions = new Map(); // 使用Map存储当前每个非零值的位置
                
                // 记录当前非零值的位置
                for (let i = 0; i < GRID_SIZE; i++) {
                    for (let j = 0; j < GRID_SIZE; j++) {
                        if (this.grid[i][j] !== 0) {
                            this.previousPositions.set(`${this.grid[i][j]}_${i}_${j}`, {row: i, col: j});
                        }
                    }
                }
                
                this.newTiles = [];
                this.mergedTiles = [];
                this.movedTiles = [];
                
                let moved = false;

                // 移动和合并逻辑
                if (direction === 'up' || direction === 'down') {
                    for (let j = 0; j < GRID_SIZE; j++) {
                        const column = this.grid.map(row => row[j]);
                        const result = this.mergeLine(direction === 'up' ? column : column.reverse());
                        const newColumn = result.line;
                        if (direction === 'down') newColumn.reverse();
                        
                        for (let i = 0; i < GRID_SIZE; i++) {
                            if (this.grid[i][j] !== newColumn[i]) {
                                moved = true;
                                // 处理移动的单元格
                                if (newColumn[i] !== 0 && this.grid[i][j] === 0) {
                                    // 这是一个移动到当前位置的格子，查找它的原始位置
                                    for (let k = 0; k < GRID_SIZE; k++) {
                                        if (this.previousGrid[k][j] === newColumn[i] && 
                                            !this.movedTiles.some(t => t.fromRow === k && t.fromCol === j)) {
                                            this.movedTiles.push({
                                                value: newColumn[i],
                                                fromRow: k,
                                                fromCol: j,
                                                toRow: i,
                                                toCol: j
                                            });
                                            break;
                                        }
                                    }
                                } else if (newColumn[i] !== 0 && this.grid[i][j] !== 0 && newColumn[i] !== this.grid[i][j]) {
                                    // 这是一个合并的单元格
                                    this.mergedTiles.push([i, j]);
                                }
                                this.grid[i][j] = newColumn[i];
                            }
                        }
                    }
                } else {
                    for (let i = 0; i < GRID_SIZE; i++) {
                        const row = this.grid[i].slice();
                        const result = this.mergeLine(direction === 'left' ? row : row.reverse());
                        const newRow = result.line;
                        if (direction === 'right') newRow.reverse();
                        
                        if (JSON.stringify(this.grid[i]) !== JSON.stringify(newRow)) {
                            moved = true;
                            for (let j = 0; j < GRID_SIZE; j++) {
                                if (this.grid[i][j] !== newRow[j]) {
                                    // 处理移动的单元格
                                    if (newRow[j] !== 0 && this.grid[i][j] === 0) {
                                        // 这是一个移动到当前位置的格子，查找它的原始位置
                                        for (let k = 0; k < GRID_SIZE; k++) {
                                            if (this.previousGrid[i][k] === newRow[j] && 
                                                !this.movedTiles.some(t => t.fromRow === i && t.fromCol === k)) {
                                                this.movedTiles.push({
                                                    value: newRow[j],
                                                    fromRow: i,
                                                    fromCol: k,
                                                    toRow: i,
                                                    toCol: j
                                                });
                                                break;
                                            }
                                        }
                                    } else if (newRow[j] !== 0 && this.grid[i][j] !== 0 && newRow[j] !== this.grid[i][j]) {
                                        // 这是一个合并的单元格
                                        this.mergedTiles.push([i, j]);
                                    }
                                    this.grid[i][j] = newRow[j];
                                }
                            }
                        }
                    }
                }

                if (moved) {
                    this.addNewTile();
                    this.animationInProgress = true;
                    this.animationStartTime = performance.now();
                    this.animationProgress = 0;
                    this.updateScore();
                }

                if (this.isGameOver()) {
                    setTimeout(() => {
                        alert(`游戏结束！最终得分：${this.score}`);
                    }, ANIMATION_DURATION);
                }
            }

            mergeLine(line) {
                // 移除空格
                let numbers = line.filter(x => x !== 0);
                let merged = []; // 用于跟踪合并操作
                
                // 合并相同的数字
                for (let i = 0; i < numbers.length - 1; i++) {
                    if (numbers[i] === numbers[i + 1]) {
                        numbers[i] *= 2;
                        this.score += numbers[i];
                        numbers.splice(i + 1, 1);
                        merged.push(i); // 记录合并的位置
                    }
                }
                
                // 补充空格
                while (numbers.length < GRID_SIZE) {
                    numbers.push(0);
                }
                
                return {line: numbers, merged: merged};
            }

            isGameOver() {
                // 检查是否还有空格
                for (let i = 0; i < GRID_SIZE; i++) {
                    for (let j = 0; j < GRID_SIZE; j++) {
                        if (this.grid[i][j] === 0) return false;
                    }
                }

                // 检查是否还能合并
                for (let i = 0; i < GRID_SIZE; i++) {
                    for (let j = 0; j < GRID_SIZE; j++) {
                        const current = this.grid[i][j];
                        if ((i < GRID_SIZE - 1 && current === this.grid[i + 1][j]) ||
                            (j < GRID_SIZE - 1 && current === this.grid[i][j + 1])) {
                            return false;
                        }
                    }
                }

                return true;
            }
        }

        // 创建游戏实例
        const game = new Game2048();

        // 添加计时器更新
        setInterval(() => {
            const timerElement = document.getElementById('timer');
            const currentTime = Date.now();
            const elapsedSeconds = Math.floor((currentTime - game.startTime) / 1000);
            
            if (game.gameMode === 'time_challenge' && game.timeLimit) {
                const remainingSeconds = Math.max(0, game.timeLimit - elapsedSeconds);
                timerElement.textContent = `剩余时间: ${remainingSeconds}秒`;
                
                // 添加倒计时颜色变化
                if (remainingSeconds <= 10) {
                    timerElement.style.color = '#f65e3b';
                    timerElement.style.animation = 'glow 1s infinite';
                } else {
                    timerElement.style.color = '';
                    timerElement.style.animation = '';
                }
                
                if (remainingSeconds === 0) {
                    setTimeout(() => {
                        alert(`时间到！最终得分：${game.score}`);
                        game.init();
                    }, 100);
                }
            } else {
                timerElement.textContent = `时间: ${elapsedSeconds}秒`;
            }
        }, 1000);
    </script>
</body>
</html> 